#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#if defined(OS_ANDROID)||defined(OS_LINUX_SOC)
#include <err.h>
#endif
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <sys/stat.h>
#include <libmscommon/mscommon.h>
#include <libmscommon/mstime.h>
#include <libmslog/mslog.h>
#include "msprotocol.h"
#define  FLAG "FILE"
typedef struct FILEContext {
	time_t timep_r;
	time_t timep_w;
	time_t timep_s;
	char *filename;	
} FILEContext;
static int file_close(URLContext *s);
static int file_open(URLContext *s)
{
	if(1==s->is_connected){
		return 0;
	}
	FILEContext * file_ctt=ms_null;
	ms_malloc_goto(fail, file_ctt, (sizeof(FILEContext)), "file_ctt");
	s->priv_data=file_ctt;
	
	if(ms_strncmps_eq(s->url,"file://")){
		file_ctt->filename=&s->url[strlen("file://")-1];
	}else{
		file_ctt->filename=s->url;
	}
	
	if(s->flags&FLAG_FILE_EMPTY){
		ms_remove(file_ctt->filename);
	}
	int flags=O_RDWR;
#if defined	(OS_WIN32) 
	flags|=O_BINARY;
#endif	
	if(!(s->flags&FLAG_FILE_NOCREATE)){
		flags|=O_CREAT;
	}
	if(s->flags&FLAG_FILE_APPEND){
		flags|=O_APPEND;
	}
	s->fd =ms_open(file_ctt->filename,flags,0666);
	if( s->fd == (-1) ){
		ms_errgoto(fail, "file_open(%s) failed(%d,%s)",file_ctt->filename,errno,strerror(errno) );
	}
	s->is_connected=1;
	return s->fd ;
fail:
	if(s->fd>0){
		ms_close(s->fd);
		s->fd=-1;
	}
	s->is_connected=0;
	return -1;
}

static int file_check(URLContext *s, int mask)
{
	FILEContext * file_ctt=s->priv_data;
	int ret=0;
	if (ms_access(file_ctt->filename,F_OK) < 0){
		return -1;
	}else{
		ret |=MS_FLAG_EXSIT;
	}
	if ( (mask&MS_FLAG_READ)
		&& (ms_access(file_ctt->filename, R_OK) >= 0)	){
		ret |= MS_FLAG_READ;
	}
	if ( (mask&MS_FLAG_WRITE)
		&& (ms_access(file_ctt->filename, W_OK) >= 0)	){
		ret |= MS_FLAG_WRITE;
	}
	return ret;
}

static int file_read(URLContext *s, unsigned char *buf, int size)
{
	if(0==s->is_connected){
		return -1;
	}
	FILEContext * file_ctt=s->priv_data;
	if(!(s->flags&FLAG_NOCHECKEXSIT)){
		if(ms_true==mstime_api_counter_async(&file_ctt->timep_r,3)){
			if (file_check(s,MS_FLAG_EXSIT) < 0){
				file_close(s);
				ms_errret(-1,"File(%s) no exist",s->url);
			}
		}	
	}
	int ret = ms_read(s->fd, buf, size);
	if( ret <0 ){
		ms_errret(-1, "file_read failed(%d,%s)", errno,strerror(errno) );
	}
	return ret;
}

static int file_write(URLContext *s, unsigned char *buf, int size)
{
	if(0==s->is_connected){
		return -1;
	}
	FILEContext * file_ctt=s->priv_data;
	if(!(s->flags&FLAG_NOCHECKEXSIT)){
		if(ms_true==mstime_api_counter_async(&file_ctt->timep_w,3)){
			if (file_check(s,MS_FLAG_EXSIT) < 0){
				file_close(s);
				ms_errret(-1,"File(%s) no exist",s->url);
			}
		}
	}
	int ret = ms_write(s->fd, buf, size);
	if( ret <0 ){
		ms_error("file_write failed(%d,%s)", errno,strerror(errno) );
	}
    return ret;
}

/* XXX: use llseek */
/*
pos: 1L
whence:
	<1>SEEK_SET:Beginning of file
	<2>SEEK_CUR:Current position of file pointer
	<3>SEEK_END:End of file
*/
static ms_s64 file_seek(URLContext *s, ms_s64 pos, ms_s32 whence)
{
	if(0==s->is_connected){
		return -1;
	}
	FILEContext * file_ctt=s->priv_data;
	if(!(s->flags&FLAG_NOCHECKEXSIT)){
		if(ms_true==mstime_api_counter_async(&file_ctt->timep_s,3)){
			if (file_check(s,MS_FLAG_EXSIT) < 0){
				file_close(s);
				ms_errret(-1,"File(%s) no exist",s->url);
			}
		}	
	}
	int64 ret;
	ret = ms_lseek(s->fd, pos, whence);
	if( ret == (-1) ){
		ms_error("file_seek failed(%d,%s)", errno,strerror(errno) );
	}
	return ret;
}

static int file_close(URLContext *s)
{
	FILEContext * file_ctt=s->priv_data;
	if(0==s->is_connected){
		return 0;
	}
	if(s->fd>0){
		ms_close(s->fd);
		s->fd=-1;
	}
	ms_demalloc(file_ctt);
	s->is_connected=0;
	return 0;
}

//support use file_delete function
static int file_remove(URLContext *s)
{
	char *filename=s->url;
	//Before remove,should close the fd;
	file_close(s);
	
	int ret = unlink(filename);
	if (ret < 0){
		ret = rmdir(filename);
		if (ret < 0){
			ms_error("file_delete(%s)  failed(%d,%s)", filename, errno,strerror(errno) );
		}
	}
	return ret;
}
static int file_rename(URLContext *s)
{
	char *filename=s->url;
	int ret = ms_rename(filename,s->urlctt_opt.file_opt.newname);
	if( ret == (-1) ){
		ms_error("file_rename filename,newfilename(%s,%s),file_rename failed(%d,%s)", filename,s->urlctt_opt.file_opt.newname, errno,strerror(errno) );
	}
    return ret;
}
static int file_empty(URLContext *s)
{
	char *filename=s->url;
	if( file_remove(s)<0){
		ms_errgoto(failed ,"file_remove(%s) failed", filename );
	}

	if( file_open(s)<0){
		ms_errgoto(failed ,"file_open(%s) failed", filename );
	}
	return 0;
failed:
	return -1;
}

URLProtocol ffurl_file_protocol = {
	.name		= PTLNAME_FILE,
	.longname	= PTLDES_FILE,		
	.url_open	= file_open,
	.url_read	= file_read,
	.url_write	= file_write,
	.url_close	= file_close,
	.priv_func={
		.file.url_check	= file_check,	
		.file.url_seek	= file_seek,		
		.file.url_remove	= file_remove,
		.file.url_rename	= file_rename,
		.file.url_empty	= file_empty,
	},
	.flags=STREAM_FILE_VOD,
	.next	=NULL,
};
